/*	$Id: entity.c 1097 2010-05-12 02:59:01Z phrakt $ */
/*
 * Copyright (c) 2003 Jean-Francois Brousseau <phrakt@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>

#include <canopy/log.h>
#include <canopy/mem.h>
#include <canopy/string.h>

#include "uri.h"
#include "http.h"
#include "stat.h"
#include "client.h"
#include "entity.h"
#include "private.h"



/*
 * http_entity_alloc()
 *
 * Allocate a new entity structure representing an entity of type <type>
 * .
 * Returns a pointer to the new entity structure on success, or NULL
 * on failure.
 */
http_ent_t*
http_entity_alloc(enum http_ent_type type, const char *uri, int flags)
{
	http_ent_t *ent;

	if ((ent = cnp_malloc(sizeof(*ent))) == NULL) {
		http_log_errno("failed to allocate entity");
		return (NULL);
	}
	memset(ent, 0, sizeof(*ent));

	if ((ent->ent_path = cnp_strdup(uri)) == NULL) {
		cnp_free(ent);
		return (NULL);
	}

	ent->ent_type = type;
	ent->ent_fd = -1;

	return (ent);
}

/*
 * http_entity_free()
 *
 * Free the entity structure <ent> and associated resources.
 */
void
http_entity_free(http_ent_t *ent)
{
	if (ent->ent_fd >= 0)
		(void)close(ent->ent_fd);

	if (ent->ent_uri != NULL)
		cnp_free(ent->ent_uri);

	if (ent->ent_path != NULL)
		cnp_free(ent->ent_path);

	if (ent->ent_body != NULL)
		cnp_free(ent->ent_body);

	if (ent->ent_media != NULL)
		cnp_free(ent->ent_media);

	if (ent->ent_encoding != NULL)
		cnp_free(ent->ent_encoding);

	cnp_free(ent);
}

/*
 * http_entity_stat()
 *
 * Fetch the status of the entity <ent>.
 * Returns an HTTP status code.
 */
int
http_entity_stat(http_ent_t *ent)
{
	int status = HTTP_STAT_OK;
	struct stat st;

	if ((ent->ent_type == HTTP_ENT_FILE) ||
	    (ent->ent_type == HTTP_ENT_DIR)) {
		if (stat(ent->ent_path, &st) == -1) {
			http_log_errno("failed to stat %s",
			    ent->ent_path);

			if ((errno == EACCES) || (errno == EPERM))
				status = HTTP_STAT_FORBIDDEN;
			else if ((errno == ENOENT) || (errno == ENOTDIR))
				status = HTTP_STAT_NOTFOUND;
			else if (errno == ENOMEM)
				status = HTTP_STAT_UNAVAILABLE;
			else
				status = HTTP_STAT_INTERNALERR;
		}
		else {
			/* update the entity's meta-information */
			ent->ent_size = (uint64_t)st.st_size;

			ent->ent_inode = (uint64_t)st.st_ino;

#if defined(__OpenBSD__) || defined(__FreeBSD__)
			ent->ent_ctime = st.st_ctimespec.tv_sec;
			ent->ent_atime = st.st_atimespec.tv_sec;
			ent->ent_mtime = st.st_mtimespec.tv_sec;
#elif defined(__linux__)
			ent->ent_ctime = st.st_ctime;
			ent->ent_atime = st.st_atime;
			ent->ent_mtime = st.st_mtime;
#endif

			ent->ent_uid = st.st_uid;
			ent->ent_gid = st.st_gid;
			ent->ent_mode = st.st_mode;
		}
	}

	return (status);
}

/*
 * http_entity_read()
 *
 * Read up to <len> bytes at <offset> from the body of entity <ent> and
 * store them in the destination buffer <buf>.  Any content encodings
 * set for this entity are applied before the data is read.
 * Returns the number of bytes read on success, or -1 on failure.
 */
ssize_t
http_entity_read(http_ent_t *ent, off_t offset, void *buf, size_t len)
{
	ssize_t ret = 0;

#if 0
	if (offset >= ent->ent_size) {
		http_log_err("read offset is over entity boundaries");
		return (-1);
	}
#endif

	if (ent->ent_type == HTTP_ENT_FILE) {
		if (ent->ent_fd == -1) {
			http_log_info("opening file %s", ent->ent_path);

			ent->ent_fd = open(ent->ent_path, O_RDONLY);
			if (ent->ent_fd == -1) {
				http_log_errno("failed to open %s",
				    ent->ent_path);
				return (-1);
			}
		}

		ret = pread(ent->ent_fd, buf, len, offset);
		if (ret == -1) {
		}
	}

	return (ret);
}

/*
 * http_entity_tag()
 *
 * Generate an ASCII tag string that can be used as a value for the ETag
 * HTTP header.  This value is computed by hashing one or more of the entity's
 * metadata fields, normally a combination of inode number, entity size and
 * last modification timestamp.
 * Returns a pointer to the start of <buf> on success, or NULL on failure.
 */
char*
http_entity_tag(http_ent_t *ent, char *buf, size_t len)
{
	snprintf(buf, len, "%llx-%llx-%x", ent->ent_inode,
	    ent->ent_size, (unsigned int)ent->ent_mtime);

	return (buf);
}

/*
 * http_entity_getsize()
 *
 * Stores the size in bytes of the entity <ent> in the area pointed to by the
 * argument <sizep>.
 *
 * Returns 0 on success, or -1 on failure.
 */
int
http_entity_getsize(http_ent_t *ent, uint64_t *sizep)
{
	*sizep = ent->ent_size;

	return (0);
}

/*
 * http_entity_gettype()
 *
 * Returns the type of the entity <ent>.
 */
enum http_ent_type
http_entity_gettype(http_ent_t *ent)
{
	return (ent->ent_type);
}
